package operations;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import util.Randomness;

import model.CUT;
import model.Filter;
import model.StatementKind;
import model.TestClusterBuilder;
import model.MethodDeclared;
public class InvokeMethod extends Operation{
	private  String _return;
	private final String _this;
	private final StatementKind method;
	private final List<Object> values;
	private  List<Class<?>> replacementObjectParam= new LinkedList<Class<?>>();

	public InvokeMethod(String _return, String _this, StatementKind method, List<Object> values,List<Class<?>> replacementObjectParam) {
		this._return = _return;
		this._this = _this;
		this.method = method;
		this.values = values;
		this.replacementObjectParam=replacementObjectParam;
	}
	
	public static List<Operation> generate(CUT clazz, TestClusterBuilder TestCluster){
//		if(clazz.getClazz().getName().equals("java.util.Locale"))
//				System.out.println("InvokeMethod.generate: "+clazz.getClazz() );
		Operation.replacementObjectParam.clear();
		List<Operation> sequence=new LinkedList<Operation>();
		Map<String, String[]> refFactory = TestCluster.getRefFactory();
	//	Map<String,Class<?>> classToTest=TestCluster.getClassToTest();
	//	if(!classToTest.containsValue(clazz.getClazz())) return null;
		if(clazz.getMethods().length==0) return null;

		StatementKind methodToTest = clazz.getMethods()[Randomness.nextRandomInt(clazz.getMethods().length)];
		List<Object> values=new LinkedList<Object>();//Operation.selectInputs(methodToTest,TestCluster);
		List<Class<?>> replacementObjectParam= new LinkedList<Class<?>>();
		   List<Class<?>> inputClasses = methodToTest.getInputTypes();
		   for (int i = 0; i < inputClasses.size(); i++) {
			      Class<?> t = inputClasses.get(i);
					if (Filter.isPrimitiveOrStringType(t))
						values.add(ValuesGenerator.generatePrimitiveValue(t));
					else
						if(t.isArray()){
							Object ref=null;
							if  (ValuesGenerator.getBoolean(Operation.GEN_NEW)){
								Operation creatArry=Operation.createArray(t,TestCluster);
								if(creatArry!=null){
									sequence.add(creatArry);
									ref= ((CreateArray)creatArry).getReference();
								}
							}
							else
								 ref=generateReference(t, TestCluster);
							values.add(ref);
						}
					else
						values.add( generateReference(t, TestCluster));
		   }
		String _this = (((MethodDeclared) methodToTest).isStatic()) ? clazz.getClazz().getName() : getReference(clazz.getClazz().getName(), refFactory);
		
		String _return = (((MethodDeclared) methodToTest).isVoid()) ? null : getReference(((MethodDeclared) methodToTest).getMethod().getReturnType().getName(), refFactory);
		if(_return==null){
			Class<?> rType=((MethodDeclared) methodToTest).getMethod().getReturnType();
			if(!rType.getName().contains("java"))
			if(!rType.getName().startsWith("sun"))
			if(!rType.getName().contains("$")) 
				if(!((MethodDeclared) methodToTest).isVoid()&&!Filter.isPrimitiveOrStringType(rType)){
						Object clz2= generateReference(rType, TestCluster);
						if(clz2!=null)
							_return=clz2.toString();	
				}
		}


		sequence.add( new InvokeMethod(_return,_this,methodToTest,values,new LinkedList<Class<?>>(Operation.replacementObjectParam)));
		return sequence;
	}
	public static List<Operation> recommendedgenerate(Class<?> clz,StatementKind methodToTest, TestClusterBuilder TestCluster){
		List<Operation> sequence=new LinkedList<Operation>();
		CUT cut=TestCluster.getCluster().get(clz.getName());
		Map<String, String[]> refFactory = TestCluster.getRefFactory();
		if(!(methodToTest instanceof MethodDeclared)) return null;

		List<Object> values=new LinkedList<Object>();//Operation.selectInputs(methodToTest,TestCluster);
		Operation.replacementObjectParam.clear();
		   List<Class<?>> inputClasses = methodToTest.getInputTypes();
		   for (int i = 0; i < inputClasses.size(); i++) {
			      Class<?> t = inputClasses.get(i);
					if (Filter.isPrimitiveOrStringType(t))
						values.add(ValuesGenerator.generatePrimitiveValue(t));
					else
						if(t.isArray()){
							Object ref=null;
							if  (ValuesGenerator.getBoolean(Operation.GEN_NEW)){
								Operation creatArry=Operation.createArray(t,TestCluster);
								if(creatArry!=null){
									sequence.add(creatArry);
									ref= ((CreateArray)creatArry).getReference();
								}
							}
							else
								 ref=generateReference(t, TestCluster);
							values.add(ref);
						}
					else
						values.add( generateReference(t, TestCluster));
		   }

		String _this = (((MethodDeclared) methodToTest).isStatic()) ? cut.getClazz().getName() : getReference(cut.getClazz().getName(), refFactory);
		
		String _return = (((MethodDeclared) methodToTest).isVoid()) ? null : getReference(((MethodDeclared) methodToTest).getMethod().getReturnType().getName(), refFactory);


		sequence.add( new InvokeMethod(_return,_this,methodToTest,values,new LinkedList<Class<?>>(Operation.replacementObjectParam)));
		return  sequence;
	}

	public  StatementKind getMethod(){
		return this.method;
	}
	 public void setValues(List<Object> newValues,List<Class<?>> replacementObjectParam ){
		  this.replacementObjectParam.clear();
		  this.replacementObjectParam.addAll(replacementObjectParam);
		  this.values.clear();
		  this.values.addAll(newValues);
	 }
	 public List<Class<?>> getReplacementObjectParam(){
		 return this.replacementObjectParam;
	 }

	 public List<Object> getValues(){
		 return this.values;
	 }
	 public String getReturn(){
		 return this._return;
	 }
	 public void  setReturn(String newRef_){
		 this._return=newRef_;
	 }
		public String getThis(){
			 return this._this;	 
		 }

	@Override
	public String toString(){
	     StringBuilder sb = new StringBuilder();
			StatementKind methodToTest =this.method;
	//		System.err.println("InvokeMethod.toString(): "+ this.method.convertString());
	     	if(this._return!=null){
	     		String rtrnTpe=((MethodDeclared) methodToTest).getOutputType().getName();	     		
		    	 sb.append( this._return + " = "); 
	     	}
	     	
	    sb.append(this._this + ".");
	     sb.append(((MethodDeclared) methodToTest).getMethodName() +"(");
	     List<Object> values = this.getValues();
	     List<Class<?>> inputType =((MethodDeclared) methodToTest).getInputTypes();
	     int indx_replacment=0;
	     for(int j=0;j<values.size();j++){
	    	 Class<?> valueClass=inputType.get(j);
	    	 Object value=values.get(j);
	    	 if(Filter.isPrimitiveOrStringType(valueClass)||value==null)
	    		 sb.append(ValuesGenerator.toCodeString(valueClass,value));
	    	 else{
	    		if(valueClass.isArray())
	    			sb.append(value);
	    		else
	    		 sb.append(ValuesGenerator.toCodeString(this.replacementObjectParam.get(indx_replacment),value));	    
	    		 indx_replacment++;

	    	 }
	    	 
	         if (j < (values.size() - 1))
	           sb.append(",");
	       }

	     sb.append(");");   
	     return sb.toString();

		}
	@Override
	public Operation clone() {
	//	List<Class<?>> cloneReplacemenetObject=new ArrayList<Class<?>>(this.replacementObjectParam);
		List<Object> cloneValues = new ArrayList<Object>(this.values);
		final InvokeMethod clone = new InvokeMethod(this._return, this._this, this.method, cloneValues,new ArrayList<Class<?>>(this.replacementObjectParam));
		return clone;
	}

}
